// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: linchuan

#include "coin2/exchange/ftx_common/api_util/auth_key.h"

#include <boost/algorithm/string/replace.hpp>

#include "coin2/base/time.h"
#include "coin2/exchange/base/api_util/api_signature.h"
#include "coin2/exchange/base/api_util/json_writer.h"

std::string FtxBaseAuth::GetCurrTimeSeconds() {
  return std::to_string(GetCurrentTimestamp() / 1000000);
}

std::string FtxBaseAuth::GetSignature(
    const std::string& secret_key,
    const std::string& address,
    const std::string& method,
    const std::string& timestamp,
    const std::string& param) {
  if (secret_key.empty()) {
    throw std::runtime_error("[ftx] secret key is empty");
  }
  std::string cre = timestamp + method + address + param;
  return ApiSignatureUtil::GetSignatureByHexHmacSHA256(cre, secret_key);
}

std::string FtxWsAuth::CreateSignature() const {
  std::string args;
  std::string ts_str = GetCurrTimeSeconds();
  std::string signa =
      ApiSignatureUtil::GetSignatureByHexHmacSHA256(ts_str + "websocket_login", key_.secret_key());
  if (!key_.owner().empty() && key_.account_type() != "master") {
    args = fmt::format(
        R"({{"key": "{}", "time": {}, "sign": "{}", "subaccount": "{}"}})",
        key_.access_key(),
        ts_str,
        signa,
        key_.owner());
  } else {
    args = fmt::format(
        R"({{"key": "{}", "time": {}, "sign": "{}"}}")",
        key_.access_key(),
        ts_str,
        signa);
  }

  return fmt::format(R"({{"op": "login", "args": {}}})", args);
}

std::vector<std::pair<std::string, std::string>> FtxRestAuth::GetHeaderSet(
    const std::string& address,
    const std::string& method,
    const std::string& param) const {
  std::vector<std::pair<std::string, std::string>> out;
  std::string ts_str = GetCurrTimeSeconds();
  std::string signa = GetSignature(key_.secret_key(), address, method, ts_str, param);
  out.push_back(std::make_pair("FTX-KEY", key_.access_key()));
  out.push_back(std::make_pair("FTX-SIGN", signa));
  out.push_back(std::make_pair("FTX-TS", ts_str));
  if (!key_.owner().empty() && key_.account_type() != "master") {
    out.push_back(std::make_pair("FTX-SUBACCOUNT", key_.owner()));
  }
  return out;
}

AsioHttpContext* FtxRestAuth::CreateRequestByGetWithSignature(
    const std::string& address,
    UrlParamsBuilder* builder) {
  std::string query_string = builder->ComputeQueryString();
  CHECK_THROW(builder->ComputePostBody().empty()) << "It is GET Method, No Payload";
  std::string target = address;
  if (!query_string.empty()) {
    target += "?" + query_string;
  }
  return AsioHttpContext::NewGet(host_, port_, bind_ip_, target, GetHeaderSet(target, "GET", ""));
}

AsioHttpContext* FtxRestAuth::CreateRequestByPostWithSignature(
    const std::string& address,
    UrlParamsBuilder* builder) {
  std::string query_string = builder->ComputeQueryString();
  std::string post_body = ComputeFtxPostBody(builder);
  std::string target = address;
  if (!query_string.empty()) {
    target += "?" + query_string;
  }
  return AsioHttpContext::NewPost(
      host_,
      port_,
      bind_ip_,
      target,
      GetHeaderSet(target, "POST", post_body),
      post_body);
}

AsioHttpContext* FtxRestAuth::CreateRequestByDeleteWithSignature(
    const std::string& address,
    UrlParamsBuilder* builder) {
  std::string query_string = builder->ComputeQueryString();
  std::string post_body = ComputeFtxPostBody(builder);
  std::string target = address;
  if (!query_string.empty()) {
    target += "?" + query_string;
  }
  return AsioHttpContext::NewDelete(
      host_,
      port_,
      bind_ip_,
      target,
      GetHeaderSet(target, "DELETE", post_body),
      post_body);
}

std::string FtxRestAuth::ComputeFtxPostBody(UrlParamsBuilder* builder) {
  std::string post_body = builder->ComputePostBody();
  // True/False cannot have quotes
  boost::replace_all(post_body, R"("True")", "true");
  boost::replace_all(post_body, R"("true")", "true");
  boost::replace_all(post_body, R"("False")", "false");
  boost::replace_all(post_body, R"("false")", "false");
  return post_body;
}
